package com.ib.review.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.TypeReference;
import com.ib.review.base.exception.ServiceException;
import com.ib.review.base.response.FileResponse;
import com.ib.review.base.response.StatusCode;
import com.ib.review.base.utils.*;
import com.ib.review.constant.CommonConstant;
import com.ib.review.pojo.dto.CompareParam;
import com.ib.review.pojo.dto.CompareRecordQuery;
import com.ib.review.pojo.dto.CompareResultQuery;
import com.ib.review.pojo.dto.CompareRoleParam;
import com.ib.review.pojo.po.CompareRecordPO;
import com.ib.review.pojo.po.CompareResultPO;
import com.ib.review.pojo.po.CompareRulePO;
import com.ib.review.pojo.vo.CompareRecordVO;
import com.ib.review.pojo.vo.CompareRuleVO;
import com.ib.review.pojo.vo.HeaderNode;
import com.ib.review.repository.CompareRepository;
import com.ib.review.service.CompareService;
import com.ib.review.util.RedisUtil;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
public class CompareServiceImpl implements CompareService {
    @Resource
    private ExcelHeaderParser excelHeaderParser;
    @Resource
    private MinioService minioService;

    @Value("${minio.parse-result}")
    private String parseResult;
    @Value("${minio.folder-path}")
    private String folderPath;

    @Value("${storage-type}")
    private String storageType;

    @Resource
    private UploadFiles uploadFiles;
    @Value("${file-system.username}")
    private String username;
    @Value("${file-system.compare-create}")
    private String compareCreate;

    @Resource
    private RedisUtil redisUtil;

    private CompareRepository compareRepository;
    @Autowired
    private void setCompareRepository(CompareRepository compareRepository) {
        this.compareRepository = compareRepository;
    }

    @Transactional
    public void upload(MultipartFile file, CompareParam param) {
        if (file.isEmpty()) {
            throw new ServiceException(StatusCode.FILE_IS_NULL);
        }
        if (!isExcelFile(file)) {
            throw new ServiceException(StatusCode.EXCEL_ERROR);
        }
        // 解析表头
        Map<String, List<HeaderNode>> result;
        try {
            result = excelHeaderParser.parseExcel(file);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        String fileName = file.getOriginalFilename();
        if(fileName == null || fileName.isEmpty()){
            throw new ServiceException(StatusCode.FILENAME_ISNULL);
        }
        String suffix = fileName.substring(fileName.lastIndexOf("."));
        String filePath;
        // 上传文件
        if("1".equals(storageType)) {
            filePath = "/" + parseResult + "/" + folderPath + "/" + param.getRuleId() + suffix;
            try {
                minioService.uploadFile(file,parseResult,folderPath,param.getRuleId() + suffix);
            } catch (Exception e) {
                throw new ServiceException(StatusCode.MINIO_UPLOAD_FAIL);
            }
        } else {
            filePath = "/" + username + compareCreate + "/" + param.getRuleId() + suffix;
            // 获取登录文件系统令牌
            String onlineID = redisUtil.get(CommonConstant.FILE_SYSTEM_LOGIN);
            if(StringUtils.isBlank(onlineID)){
                FileResponse fileResponse = uploadFiles.getOnlineID();
                onlineID = fileResponse.getUserOnlineID();
                Integer tokenExpireTime = fileResponse.getTokenExpireTime();
                redisUtil.setex(CommonConstant.FILE_SYSTEM_LOGIN, onlineID, tokenExpireTime-20, TimeUnit.SECONDS);
            }
            // 校验是否创建文件夹
            if(!"1".equals(redisUtil.get(CommonConstant.FILE_SYSTEM_COMPARE))){
                uploadFiles.createDirectory(compareCreate,onlineID);
                redisUtil.set(CommonConstant.FILE_SYSTEM_COMPARE,"1");
            }
            try {
                uploadFiles.uploadWithRestTemplate(file,filePath,onlineID);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }

        Date currentDate = getCurrentDateWithoutMillis();
        String userName = StpUtil.getTokenSession().getString("userAccount");

        Long count = compareRepository.selectCountByRecordId(param.getRecordId());
        if(count == 0){
            CompareRecordPO po = new CompareRecordPO();
            po.setRecordId(param.getRecordId());
            po.setStatus(0);
            po.setCreator(userName);
            po.setCreateTime(currentDate);
            po.setModifier(userName);
            po.setModifyTime(currentDate);
            po.setDelFlag(0);
            compareRepository.insertRecord(po);
        }

        // 删除已存在的规则
        compareRepository.deleteRule(param.getRuleId());

        CompareRulePO po1 = new CompareRulePO();
        po1.setRuleId(param.getRuleId());
        po1.setRecordId(param.getRecordId());
        po1.setRuleType(param.getRuleType());
        po1.setFileName(fileName);
        po1.setFilePath(filePath);
        po1.setHeading(JSON.toJSONString(result));
        po1.setCreator(userName);
        po1.setCreateTime(currentDate);
        po1.setModifier(userName);
        po1.setModifyTime(currentDate);
        po1.setDelFlag(0);
        compareRepository.insertRule(po1);

        // 维护主记录表的文件名
        List<String> list = compareRepository.queryFileName(param.getRecordId());
        String Names = String.join(",", list);
        compareRepository.updateFileNames(param.getRecordId(),Names);

    }

    public Map<String,Map<String, List<HeaderNode>>> queryHeading(CompareParam param) {
        param.setRuleType(1);
        String rule1 = compareRepository.queryHeading(param);
        Map<String, List<HeaderNode>> map1 = JSON.parseObject(rule1, new TypeReference<>(){});
        param.setRuleType(2);
        String rule2 = compareRepository.queryHeading(param);
        Map<String, List<HeaderNode>> map2 = JSON.parseObject(rule2, new TypeReference<>(){});
        Map<String,Map<String, List<HeaderNode>>> map = new HashMap<>();
        map.put("benchmark_file",map1);
        map.put("compare_file",map2);
        return map;
    }

    public void addRole(CompareRoleParam param) {
        String userName = StpUtil.getTokenSession().getString("userAccount");
        param.setModifier(userName);
        param.setModifyTime(getCurrentDateWithoutMillis());
        compareRepository.updateRoleContent(param);
    }

    public List<CompareResultPO> queryResult(CompareResultQuery compareResultQuery) {
        return compareRepository.selectByRecordId(compareResultQuery);
    }

    public List<CompareRecordPO> queryRecord(CompareRecordQuery compareRecordQuery) {
        // 直接计算三天前的时间，去除纳秒部分
        LocalDateTime threeDaysAgo = LocalDateTime.now().minusDays(2).withNano(0);
        String fileName = null;
        if (compareRecordQuery != null) {
            fileName = compareRecordQuery.getFileNames();
        }
        String userName = StpUtil.getTokenSession().getString("userAccount");
        return compareRepository.selectByTimeRange(threeDaysAgo,fileName,userName);
    }

    public CompareRecordVO queryRecordById(CompareRecordQuery compareRecordQuery) {
        CompareRecordPO po = compareRepository.queryRecordById(compareRecordQuery);
        if(po != null){
            CompareRecordVO vo = new CompareRecordVO();
            BeanUtils.copyProperties(po,vo);
            // 获取比对文件信息
            CompareRulePO po1 = compareRepository.queryRuleById(compareRecordQuery.getRecordId(),
                    CommonConstant.BENCHMARK_FILE_TYPE);
            if(po1 != null) {
                CompareRuleVO vo1 = new CompareRuleVO();
                BeanUtils.copyProperties(po1, vo1);
                vo.setCompareFile(vo1);
            }
            // 获取基准文件信息
            CompareRulePO po2 = compareRepository.queryRuleById(compareRecordQuery.getRecordId(),
                    CommonConstant.COMPARE_FILE_TYPE);
            if(po2 != null) {
                CompareRuleVO vo2 = new CompareRuleVO();
                BeanUtils.copyProperties(po2, vo2);
                vo.setBenchmarkFile(vo2);
            }
            return vo;
        }
        return null;
    }

    public byte[] exportToExcel(Map<String, Object> requestData) {
        // 转换请求数据格式
        Map<String, List<List<Map<String, Object>>>> data = convertRequestData(requestData);
        byte[] bytes;
        try {
            bytes = CompareExcelExporter.exportTablesToExcel(data);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return bytes;
    }

    private Date getCurrentDateWithoutMillis() {
        LocalDateTime now = LocalDateTime.now().withNano(0);
        return Date.from(now.atZone(ZoneId.systemDefault()).toInstant());
    }

    private boolean isExcelFile(MultipartFile file) {

        // 验证文件类型（Excel）
        String originalFilename = file.getOriginalFilename();
        String contentType = file.getContentType();

        // 允许的Excel扩展名
        List<String> allowedExtensions = Arrays.asList(".xlsx", ".xls");
        // 允许的Excel MIME类型
        List<String> allowedContentTypes = Arrays.asList(
                "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", // .xlsx
                "application/vnd.ms-excel", // .xls
                "application/octet-stream" // 某些浏览器可能返回的通用二进制类型
        );

        // 验证扩展名
        boolean extensionValid = false;
        if(originalFilename != null) {
            extensionValid = allowedExtensions.stream()
                    .anyMatch(ext -> originalFilename.toLowerCase().endsWith(ext));
        }

        // 验证Content-Type
        boolean contentTypeValid = allowedContentTypes.contains(contentType);

        return extensionValid && contentTypeValid;
    }

    // 数据格式转换方法
    @SuppressWarnings("unchecked")
    private Map<String, List<List<Map<String, Object>>>> convertRequestData(Map<String, Object> rawData) {
        Map<String, List<List<Map<String, Object>>>> result = new LinkedHashMap<>();

        // 假设原始数据格式：{"基准与比对共有": [[{...}, {...}], ...}
        rawData.forEach((key, value) -> result.put(key, (List<List<Map<String, Object>>>) value));

        return result;
    }
}
